package goroutine

import "time"

// 空闲工作协程管理器-队列
type idleMngQueue struct {
	// 空闲列表
	idleList []*worker

	// 队列大小
	size int

	// 头节点位置
	head int

	// 尾节点位置
	tail int

	// 队列是否已满
	isFull bool
}

// 计算空闲工作协程数量
func (q *idleMngQueue) Len() int {
	if q.size < 1 {
		return 0
	}

	if q.head == q.tail {
		if q.isFull {
			return q.size
		}
		return 0
	}

	if q.tail > q.head {
		return q.tail - q.head
	}

	return q.size - q.head + q.tail
}

// 检查队列是否为空
func (q *idleMngQueue) IsEmpty() bool {
	return q.Len() == 0
}

// 检查队列是否已满
func (q *idleMngQueue) IsFull() bool {
	return q.isFull
}

// 获取一个空闲工作协程
func (q *idleMngQueue) GetWorker() *worker {
	if q.IsEmpty() {
		return nil
	}

	worker := q.idleList[q.head]
	q.idleList[q.head] = nil
	q.head++
	if q.head == q.size {
		q.head = 0
	}
	q.isFull = false

	return worker
}

// 放回一个空闲工作协程
func (q *idleMngQueue) PutWorker(worker *worker) {
	q.idleList[q.tail] = worker

	q.tail++
	if q.tail == q.size {
		q.tail = 0
	}

	if q.tail == q.head {
		q.isFull = true
	}
}

// 清除过期工作协程
func (q *idleMngQueue) Expire(duration time.Duration) (list []*worker) {
	startTime := time.Now().Add(-duration)
	index := q.binarySearch(startTime)
	if index == -1 {
		return nil
	}

	if q.head <= index {
		list = append(list, q.idleList[q.head:index+1]...)
		for i := q.head; i < index+1; i++ {
			q.idleList[i] = nil
		}
	} else {
		list = append(list, q.idleList[:index+1]...)
		list = append(list, q.idleList[q.head:]...)
		for i := 0; i < index+1; i++ {
			q.idleList[i] = nil
		}
		for i := q.head; i < q.size; i++ {
			q.idleList[i] = nil
		}
	}

	q.head = (index + 1) % q.size
	if len(list) > 0 {
		q.isFull = false
	}

	return list
}

// 清空队列
func (q *idleMngQueue) Clear() (list []*worker) {
	for !q.IsEmpty() {
		list = append(list, q.GetWorker())
	}

	q.size = 0
	q.head = 0
	q.tail = 0

	return list
}

// 查找超时空闲工作协程-二分查找
func (q *idleMngQueue) binarySearch(startTime time.Time) int {
	// 没有空闲协程 或 头节点未过期
	if q.IsEmpty() || q.idleList[q.head].idleTime.After(startTime) {
		return -1
	}

	// size = 10 head = 8 tail = 4
	// [ 3, 4, 5, 6, nil, nil, nil, nil, 1, 2 ] 实际情况
	//   0  1  2  3  4    5    6    7    8  9
	//            r                      l

	middle, index, left, right := 0, 0, q.head, q.tail-1
	if right < left {
		right = q.tail + q.size - 1
	}
	for left <= right {
		middle = (left + right) / 2
		index = middle % q.size
		if q.idleList[index].idleTime.After(startTime) {
			right = middle - 1
		} else {
			left = middle + 1
		}
	}

	return right % q.size
}

// 创建空闲工作协程管理器-队列
func newIdleMngQueue(size int) *idleMngQueue {
	queue := new(idleMngQueue)
	queue.size = size
	queue.idleList = make([]*worker, size)
	return queue
}
